#include <ne.h>
#include <system/GlutApplication.h>

int Width = 1280;
int Height = 720;
int HalfWidth = 640;
int HalfHeight = 360;

float SkyTextureSize = 512.0f;
float SkyBoxSize = 1024.0f;

std::string skyBoxImageMap[6] = {"../image/skybox/evening_RT.jpg",
                                 "../image/skybox/evening_LF.jpg",
                                 "../image/skybox/evening_UP.jpg",
                                 "../image/skybox/evening_DN.jpg",
                                 "../image/skybox/evening_BK.jpg",
                                 "../image/skybox/evening_FR.jpg"};

int lineMode = 0;
int alphaMode = 0;
int lightOn = 1;
ne::Engine *pEngine = 0;
ne::Light *light = 0;
ne::Timer timer;
ne::Camera *camera = 0;
ne::Viewport *viewport = 0;
ne::GlutApplication *pApplication = 0;
ne::Mesh *pMesh = 0;
ne::GLFrameBufferObject *pFrameBuffer = 0;
ne::GLFrameBufferObject *pBlurXFrameBuffer = 0;
ne::GLFrameBufferObject *pBlurYFrameBuffer = 0;
ne::TexturePtr pTexture;
ne::TexturePtr pSkyTexture;
ne::GLSLProgramPtr pProgram;
ne::GLSLProgramPtr pDOFProgram;

int sampleLocation = 0;
int sampleNum = 9;
float point = 0.2f;
float range = 0.1f;

void keyboard(unsigned char key, int x, int y);
void keyboardUp(unsigned char key, int x, int y);
void loop(void);
void redraw(void);
void drawFPS(void);
void resize(int width, int height);
void init();

int main(int argc, char *argv[]) /* I - Command-line arguments */
{
    pApplication = new ne::GlutApplication();//(argc, argv);
    glutReshapeFunc(resize);
    glutDisplayFunc(loop);
    glutKeyboardFunc(keyboard);

    pEngine = pApplication->engine;
    if (!pEngine || !pEngine->isReady())
    {
        delete pApplication;
        return 0;
    }

    // Create Mesh
    if (argc > 1)
    {
        /*
        ne::MeshManager *pMeshManager = pEngine->getMeshManager();
        pMesh = pMeshManager->createMeshFromFile(argv[1]);
        */
    }

    init();

    glutMainLoop();

    delete pApplication;

    return 0;
}

/*
void setLineMode(ne::UIButton *button)
{
    lineMode ^= 1;
    if (lineMode)
    {
        button->setText("Fill Mode");
    }
    else
    {
        button->setText("Line Mode");
    }
}

void setAlphaMode(ne::UIButton *button)
{
    alphaMode ^= 1;
    if (alphaMode)
    {
        button->setText("Color Mode");
    }
    else
    {
        button->setText("Alpha Mode");
    }
}

void setLightOn(ne::UIButton *button)
{
    lightOn ^= 1;
    if (lightOn)
    {
        button->setText("Light Off");
    }
    else
    {
        button->setText("Light On");
    }
}
*/

void init()
{
    camera = new ne::Camera();
    viewport = new ne::Viewport(camera);
    camera->setPosition(ne::Vector3::UNIT_Z * 200);

    glClearColor(0, 0, 0, 0);
    glClearDepth(1);
    //glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glDepthFunc(GL_LEQUAL);
    glEnable(GL_DEPTH_TEST);

    glCullFace(GL_BACK);
    //glEnable(GL_CULL_FACE);

    //glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);

    glShadeModel(GL_SMOOTH);
    glEnable(GL_POINT_SMOOTH);
    glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_LINE_SMOOTH);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    glEnable(GL_POLYGON_SMOOTH);
    glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

    pTexture = pEngine->getTextureManager()->createTextureFromFile("chess", "../image/chessboard.png", ne::TF_RGBA);
    pTexture->load();
    pTexture->setWrap(ne::TWP_REPEAT, ne::TWP_REPEAT);
    pTexture->setNumMipmap(8);
    pTexture->setFilter(ne::TFF_LINEAR_MIPMAP_LINEAR, ne::TFF_LINEAR);
    pTexture->setLOD(0, 8, -0.5f);
    pTexture->createRenderTexture();

    // Blur
    pFrameBuffer = new ne::GLFrameBufferObject();
    pFrameBuffer->bind();

    pTexture = pEngine->getTextureManager()->createTexture("FullScreen", "FullScreen", ne::TT_2D, ne::TF_RGB, ne::PT_UNSIGNED_BYTE, Width, Height);
    pTexture->createRenderTexture();
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pTexture->getSource(), 0);

    pTexture = pEngine->getTextureManager()->createTexture("DepthMap", "DepthMap", ne::TT_2D, ne::TF_DEPTH_COMPONENT, ne::PT_FLOAT, Width, Height);
    //glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
    pTexture->createRenderTexture();
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, pTexture->getSource(), 0);

    std::cout << "Framebuffer " << pFrameBuffer->checkStatus() << std::endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);


    // BlurX
    pBlurXFrameBuffer = new ne::GLFrameBufferObject();
    pBlurXFrameBuffer->bind();
    ne::GLRenderBufferObject *pRenderBuffer = new ne::GLRenderBufferObject();
    pRenderBuffer->setStorage(ne::TF_DEPTH_COMPONENT, HalfWidth, HalfHeight);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, pRenderBuffer->getSource());
    pTexture = pEngine->getTextureManager()->createTexture("BlurXScreen", "BlurXScreen", ne::TT_2D, ne::TF_RGB, ne::PT_UNSIGNED_BYTE, HalfWidth, HalfHeight);
    pTexture->createRenderTexture();
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pTexture->getSource(), 0);

    std::cout << "BlurXFramebuffer " << pBlurXFrameBuffer->checkStatus() << std::endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    // BlurY
    pBlurYFrameBuffer = new ne::GLFrameBufferObject();
    pBlurYFrameBuffer->bind();
    pRenderBuffer = new ne::GLRenderBufferObject();
    pRenderBuffer->setStorage(ne::TF_DEPTH_COMPONENT, HalfWidth, HalfHeight);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, pRenderBuffer->getSource());
    pTexture = pEngine->getTextureManager()->createTexture("BlurScreen", "BlurScreen", ne::TT_2D, ne::TF_RGB, ne::PT_UNSIGNED_BYTE, HalfWidth, HalfHeight);
    pTexture->createRenderTexture();
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pTexture->getSource(), 0);

    std::cout << "BlurYFramebuffer " << pBlurYFrameBuffer->checkStatus() << std::endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    ne::ImagePtr pImage;
    pSkyTexture = pEngine->getTextureManager()->createTexture("SkyBox", "SkyBox", ne::TT_CUBE_MAP, ne::TF_RGB, ne::PT_UNSIGNED_BYTE, SkyTextureSize, SkyTextureSize);
    for (size_t i=0; i<6; ++i)
    {
        pImage = pEngine->getImageManager()->create("SkyBoxImage", skyBoxImageMap[i]);
        pImage->load();
        pSkyTexture->loadImageForCubeMap(ne::TextureCubeMapFace(ne::TCMF_POSITIVE_X + i), *pImage);
        pEngine->getImageManager()->removeByPtr(pImage);
    }
    pSkyTexture->createRenderTexture();

    camera->setFarClipDistance(2048.0f);


    // Blur shader
    ne::GLSLManager *pManager = pEngine->getGLSLManager();
    pProgram = pManager->create("Blur", "../shader/blur.glpg", true);
    ne::GLSLShaderPtr pVShader;
    pVShader = pProgram->createShaderFromFile("BlurVertexShader", "../shader/blur.glvs", ne::SHADER_TYPE_VERTEX);
    ne::GLSLShaderPtr pFShader;
    pFShader = pProgram->createShaderFromFile("BlurFragmentShader", "../shader/blurDirection.glfs", ne::SHADER_TYPE_FRAGMENT);
    pProgram->attachShader(pVShader);
    pProgram->attachShader(pFShader);
    pProgram->link();

    // DOF shader
    pDOFProgram = pManager->create("DOF", "../shader/depth_of_field.glpg", true);
    pVShader = pProgram->createShaderFromFile("DOFVertexShader", "../shader/depth_of_field.glvs", ne::SHADER_TYPE_VERTEX);
    pFShader = pProgram->createShaderFromFile("DOFFragmentShader", "../shader/depth_of_field.glfs", ne::SHADER_TYPE_FRAGMENT);
    pDOFProgram->attachShader(pVShader);
    pDOFProgram->attachShader(pFShader);
    pDOFProgram->link();

    // Set light
    light = new ne::Light();
    light->setType(ne::LT_POINT);
    light->setAmbient(ne::Color::GRAY);
    light->setAttenuationFactor(ne::LAT_LINEAR, 0.01f);
    light->setPosition(ne::Vector3(0.0f, 0.0f, 500.0f));
}

void keyboard(unsigned char key, int x, int y)
{
    if (key == 'w' || key == 'W')
    {
        camera->translate(ne::Vector3::NEGATIVE_UNIT_Z, ne::TS_LOCAL);
    }
    else if (key == 's' || key == 'S')
    {
        camera->translate(ne::Vector3::UNIT_Z, ne::TS_LOCAL);
    }
    else if (key == 'a' || key == 'A')
    {
        camera->translate(ne::Vector3::NEGATIVE_UNIT_X, ne::TS_LOCAL);
    }
    else if (key == 'd' || key == 'D')
    {
        camera->translate(ne::Vector3::UNIT_X, ne::TS_LOCAL);
    }
    else if (key == 'o' || key == 'O')
    {
        camera->translate(ne::Vector3::NEGATIVE_UNIT_Y, ne::TS_PARENT);
    }
    else if (key == 'u' || key == 'U')
    {
        camera->translate(ne::Vector3::UNIT_Y, ne::TS_PARENT);
    }
    else if (key == 'q' || key == 'Q')
    {
        camera->yaw(ne::Math::PI / 36);
    }
    else if (key == 'e' || key == 'E')
    {
        camera->yaw(- ne::Math::PI / 36);
    }
    else if (key == 'i' || key == 'I')
    {
        camera->pitch(ne::Math::PI / 36);
    }
    else if (key == 'k' || key == 'K')
    {
        camera->pitch(- ne::Math::PI / 36);
    }
    else if (key == 'j' || key == 'J')
    {
        camera->roll(ne::Math::PI / 36);
    }
    else if (key == 'l' || key == 'L')
    {
        camera->roll(- ne::Math::PI / 36);
    }
    else if (key == 'n' || key == 'N')
    {
        lineMode ^= 1;
    }
    else if (key == 'p' || key == 'P')
    {
        std::cout << camera << std::endl;
        std::cout << "Position : " << camera->getPosition() << std::endl;
        std::cout << "Direction : " << camera->getDirection() << std::endl;
        std::cout << "Orientation : " << camera->getOrientation() << std::endl;
    }
    else if (key == 0x1b)
    {
        exit(1);
    }
}

void keyboardUp(unsigned char key, int x, int y)
{
}

void loop(void)
{
    redraw();
}

void drawSky(void)
{
    glDisable(GL_DEPTH_TEST);
    glDepthMask(GL_FALSE);
    glDisable(GL_LIGHTING);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(pSkyTexture->getType(), pSkyTexture->getSource());
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glEnable(pSkyTexture->getType());

    glBegin(GL_QUADS);
        glTexCoord3f(-1.0f, 1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, SkyBoxSize);
        glTexCoord3f(-1.0f, -1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, SkyBoxSize);
        glTexCoord3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(-1.0f, 1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, -SkyBoxSize);

        glTexCoord3f(1.0f, 1.0f, 1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, SkyBoxSize);
        glTexCoord3f(1.0f, 1.0f, -1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, -1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, 1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, SkyBoxSize);

        glTexCoord3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(-1.0f, -1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, 1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, -1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, -SkyBoxSize);

        glTexCoord3f(-1.0f, 1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(1.0f, 1.0f, -1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(1.0f, 1.0f, 1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, SkyBoxSize);
        glTexCoord3f(-1.0f, 1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, SkyBoxSize);

        glTexCoord3f(1.0f, 1.0f, -1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(-1.0f, 1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(-1.0f, -1.0f, -1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, -SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, -1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, -SkyBoxSize);

        glTexCoord3f(1.0f, 1.0f, 1.0f);
        glVertex3f(SkyBoxSize, SkyBoxSize, SkyBoxSize);
        glTexCoord3f(1.0f, -1.0f, 1.0f);
        glVertex3f(SkyBoxSize, -SkyBoxSize, SkyBoxSize);
        glTexCoord3f(-1.0f, -1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, -SkyBoxSize, SkyBoxSize);
        glTexCoord3f(-1.0f, 1.0f, 1.0f);
        glVertex3f(-SkyBoxSize, SkyBoxSize, SkyBoxSize);
    glEnd();
    glDisable(pSkyTexture->getType());

    glEnable(GL_DEPTH_TEST);
    glDepthMask(GL_TRUE);
    glEnable(GL_LIGHTING);
}

void drawMesh(void)
{
    pTexture = pEngine->getTextureManager()->getByName("chess");
    glBindTexture(pTexture->getType(), pTexture->getSource());
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glEnable(pTexture->getType());

    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    for (float z=-100.0f; z<=100.0f; z+=50.0f)
    {
        glPushMatrix();
        glTranslatef(-50.0f, 0.0f, z);
        glutSolidTeapot(10);
        glPopMatrix();
        glPushMatrix();
        glTranslatef(0.0f, 0.0f, z);
        glutSolidTeapot(10);
        glPopMatrix();
        glPushMatrix();
        glTranslatef(50.0f, 0.0f, z);
        glutSolidTeapot(10);
        glPopMatrix();
    }
    glDisable(pTexture->getType());
}

void redraw(void)
{
    glEnable(GL_FRAMEBUFFER);

    pFrameBuffer->bind();

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    const ne::Matrix4 &viewMatrix = camera->getViewMatrix();
    glLoadMatrixf(viewMatrix.ptr());

    glPushMatrix();
        ne::Vector3 pos = camera->getPosition();
        glTranslatef(pos.x, pos.y, pos.z);
        drawSky();
    glPopMatrix();

    if (lightOn)
    {
        glEnable(GL_LIGHTING);
    }
    glEnable(GL_BLEND);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_COLOR_SUM);

    if (lineMode)
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }
    else
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }

    if (alphaMode)
    {
        ne::RenderSystem::GetSingleton().setDepthWriteEnable(false);
        glColor4f(0.0f, 0.5f, 1.0f, 0.5f);
    }
    else
    {
        ne::RenderSystem::GetSingleton().setDepthWriteEnable(true);
        glColor4f(0.5f, 0.5f, 0.5f, 1.0f);
    }

    glPushMatrix();
        light->setPosition(camera->getPosition());
        light->setOrientation(camera->getOrientation());
        light->useLight();
        glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
        glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ne::Color::WHITE.ptr());

        drawMesh();

    glPopMatrix();

    glDisable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_COLOR_MATERIAL);
    glDisable(GL_COLOR_SUM);
    glDisable(GL_LIGHTING);

    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    glDisable(GL_FRAMEBUFFER);

    drawFPS();

    glutSwapBuffers();
    glutPostRedisplay();
}

void resize(int width, int height)
{
    /* Save the new width and height */
    Width  = width;
    Height = height;

    /* Reset the viewport->.. */
    glViewport(0, 0, width, height);
    viewport->setPosition(0, 0);
    viewport->resize(width, height);

    glMatrixMode(GL_PROJECTION);
    const ne::Matrix4 &projectionMatrix = camera->getProjectionMatrix();
    glLoadMatrixf(projectionMatrix.ptr());
    //gluPerspective(45, double(width)/double(height), 1.0, 1000.0);
    glMatrixMode(GL_MODELVIEW);
}

void drawFPS()
{
    //Set matrices for ortho
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	glOrtho(0.0f, Width,
            Height, 0.0f,
            1.0f, -1.0f);

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

    glViewport(0, 0, HalfWidth, HalfHeight);

    // Second pass
    glEnable(GL_FRAMEBUFFER);

    pBlurXFrameBuffer->bind();

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    pProgram->useProgram();
    pProgram->setUniform("sampleNum", sampleNum);
    ne::Vector2 directionX(1.0f/float(HalfWidth), 0.0f);
    pProgram->setUniform("direction", directionX);
    pProgram->setUniform("screen", 0);
    pProgram->validate();

    glColor4f(1.0, 1.0, 1.0, 1.0);
    pTexture = pEngine->getTextureManager()->getByName("FullScreen");
    glBindTexture(pTexture->getType(), pTexture->getSource());
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glEnable(pTexture->getType());
    glBegin(GL_QUADS);
    {
        glTexCoord2f(0.0f, 1.0f);
        glVertex3f(0, 0, 0);
        glTexCoord2f(0.0f, 0.0f);
        glVertex3f(0, Height, 0);
        glTexCoord2f(1.0f, 0.0f);
        glVertex3f(Width, Height, 0);
        glTexCoord2f(1.0f, 1.0f);
        glVertex3f(Width, 0, 0);
    }
    glEnd();
    glDisable(pTexture->getType());
    pProgram->useProgram(false);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    glDisable(GL_FRAMEBUFFER);

    // Third pass
    glEnable(GL_FRAMEBUFFER);

    pBlurYFrameBuffer->bind();

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    pProgram->useProgram();
    pProgram->setUniform("sampleNum", sampleNum);
    ne::Vector2 directionY(0.0f, 1.0f/float(HalfHeight));
    pProgram->setUniform("direction", directionY);
    pProgram->setUniform("screen", 0);
    pProgram->validate();

    glColor4f(1.0, 1.0, 1.0, 1.0);
    pTexture = pEngine->getTextureManager()->getByName("BlurXScreen");
    glBindTexture(pTexture->getType(), pTexture->getSource());
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glEnable(pTexture->getType());
    glBegin(GL_QUADS);
    {
        glTexCoord2f( 0.0f, 1.0f);
        glVertex3f(0, 0, 0);
        glTexCoord2f( 0.0f, 0.0f);
        glVertex3f(0, Height, 0);
        glTexCoord2f( 1.0f, 0.0f);
        glVertex3f(Width, Height, 0);
        glTexCoord2f( 1.0f, 1.0f);
        glVertex3f(Width, 0, 0);
    }
    glEnd();
    glDisable(pTexture->getType());
    pProgram->useProgram(false);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glDisable(GL_FRAMEBUFFER);

    // Forth pass
    glViewport(0, 0, Width, Height);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    pDOFProgram->useProgram();
    pDOFProgram->setUniform("point", point);
    pDOFProgram->setUniform("range", range);
    pDOFProgram->setUniform("oriMap", 0);
    pDOFProgram->setUniform("blurMap", 1);
    pDOFProgram->setUniform("depthMap", 2);
    pDOFProgram->validate();

    glColor4f(1.0, 1.0, 1.0, 1.0);
    pTexture = pEngine->getTextureManager()->getByName("FullScreen");
    glBindTexture(pTexture->getType(), pTexture->getSource());
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    glActiveTexture(GL_TEXTURE1);
    pTexture = pEngine->getTextureManager()->getByName("BlurScreen");
    glBindTexture(pTexture->getType(), pTexture->getSource());
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    
    glActiveTexture(GL_TEXTURE2);
    pTexture = pEngine->getTextureManager()->getByName("DepthMap");
    glBindTexture(pTexture->getType(), pTexture->getSource());
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    glEnable(pTexture->getType());
    glBegin(GL_QUADS);
    {
        glTexCoord2f( 0.0f, 1.0f);
        glVertex3f(0, 0, 0);
        glTexCoord2f( 0.0f, 0.0f);
        glVertex3f(0, Height, 0);
        glTexCoord2f( 1.0f, 0.0f);
        glVertex3f(Width, Height, 0);
        glTexCoord2f( 1.0f, 1.0f);
        glVertex3f(Width, 0, 0);
    }
    glEnd();
    glDisable(pTexture->getType());
    pDOFProgram->useProgram(false);

    glActiveTexture(GL_TEXTURE0);
    pTexture = pEngine->getTextureManager()->getByName("FullScreen");
    glBindTexture(pTexture->getType(), pTexture->getSource());
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glEnable(pTexture->getType());
    glBegin(GL_QUADS);
    {
        glTexCoord2f( 0.0f, 1.0f);
        glVertex3f(0, 0, 0);
        glTexCoord2f( 0.0f, 0.0f);
        glVertex3f(0, HalfHeight >> 1, 0);
        glTexCoord2f( 1.0f, 0.0f);
        glVertex3f(HalfWidth >> 1, HalfHeight >> 1, 0);
        glTexCoord2f( 1.0f, 1.0f);
        glVertex3f(HalfWidth >> 1, 0, 0);
    }
    glEnd();
    glDisable(pTexture->getType());

	//Print text
    glEnable(GL_BLEND);
    
    //Print text
    ne::FontPtr font = pEngine->getFont();
    ne::Vector3 pos(Width - 50.0f, 10.0f, 0.0f);
    float w = font->getFontWidth();
    float h = font->getFontHeight();

    char fpsString[32];
    ne::real interval = timer.getIntervalMilliseconds();
	sprintf(fpsString, "%.2f", 1000.0f / interval);

    font->renderText2D(fpsString, pos, 6.0f*w, h, ne::Color(0.8f, 1.0f, 0.0f, 1.0f));

    //reset matrices
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
}

